//
//  CCActionIntervalEx.cpp
//  airfight
//
//  Created by USER on 12-9-19.
//  Copyright (c) 2012年 moonriver. All rights reserved.
//

#include "CCActionIntervalEx.h"
#include "BulletSprite.h"
#include "EnemyFighterSprite.h"
#include "Trace.h"

//
// IntervalAction
//
CCActionIntervalEx* CCActionIntervalEx::actionWithDuration(int durationFrames)
{
    return CCActionIntervalEx::create(durationFrames);
}

CCActionIntervalEx* CCActionIntervalEx::create(int durationFrames)
{
    CCActionIntervalEx *pAction = new CCActionIntervalEx();
    pAction->initWithDuration(durationFrames);
    pAction->autorelease();
    
    return pAction;
}

bool CCActionIntervalEx::initWithDuration(int durationFrames)
{
    m_DurationFrames = durationFrames;
    if (m_DurationFrames == 0)
    {
        m_DurationFrames = FLT_EPSILON;
    }
    
    m_ElapsedFrames = 0;
    m_bFirstTick = true;
    
    return true;
}

CCObject* CCActionIntervalEx::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCActionIntervalEx* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCActionIntervalEx*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCActionIntervalEx();
        pZone = pNewZone = new CCZone(pCopy);
    }
    
    
    CCFiniteTimeActionEx::copyWithZone(pZone);
    
    CC_SAFE_DELETE(pNewZone);
    
    pCopy->initWithDuration(m_DurationFrames);
    
    return pCopy;
}

bool CCActionIntervalEx::isDone(void)
{
    return m_ElapsedFrames >= m_DurationFrames;
}

void CCActionIntervalEx::step(float dt)
{
    if (m_bFirstTick)
    {
        m_bFirstTick = false;
        m_ElapsedFrames = 0;
    }
    else
    {
        m_ElapsedFrames++;
    }
    
    this->update(MAX (0,                                  // needed for rewind. elapsed could be negative
                      MIN(1, m_ElapsedFrames /
                          MAX(m_DurationFrames, FLT_EPSILON)   // division by 0
                          )
                      )
                 );
}

void CCActionIntervalEx::setAmplitudeRate(CCFloat amp)
{
    CC_UNUSED_PARAM(amp);
    // Abstract class needs implementation
    CCAssert(0, "");
}

CCFloat CCActionIntervalEx::getAmplitudeRate(void)
{
    // Abstract class needs implementation
    CCAssert(0, "");
    
    return 0;
}

void CCActionIntervalEx::startWithTarget(CCNode *pTarget)
{
    CCFiniteTimeActionEx::startWithTarget(pTarget);
    m_ElapsedFrames = 0;
    m_bFirstTick = true;
}

//
// Sequence
//
CCSequenceEx* CCSequenceEx::actionOneTwo(CCFiniteTimeActionEx *pActionOne, CCFiniteTimeActionEx *pActionTwo)
{
    return CCSequenceEx::create(pActionOne, pActionTwo);
}

CCSequenceEx* CCSequenceEx::create(CCFiniteTimeActionEx *pActionOne, CCFiniteTimeActionEx *pActionTwo)
{
    CCSequenceEx *pSequence = new CCSequenceEx();
    pSequence->initOneTwo(pActionOne, pActionTwo);
    pSequence->autorelease();
    
    return pSequence;
}

CCFiniteTimeActionEx* CCSequenceEx::actions(CCFiniteTimeActionEx *pAction1, ...)
{
    va_list params;
    va_start(params, pAction1);
    
    CCFiniteTimeActionEx *pNow;
    CCFiniteTimeActionEx *pPrev = pAction1;
    
    while (pAction1)
    {
        pNow = va_arg(params, CCFiniteTimeActionEx*);
        if (pNow)
        {
            pPrev = CCSequenceEx::create(pPrev, pNow);
        }
        else
        {
            break;
        }
    }
    
    va_end(params);
    return pPrev;
}

CCFiniteTimeActionEx* CCSequenceEx::create(CCFiniteTimeActionEx *pAction1, ...)
{
    va_list params;
    va_start(params, pAction1);
    
    CCFiniteTimeActionEx *pNow;
    CCFiniteTimeActionEx *pPrev = pAction1;
    
    while (pAction1)
    {
        pNow = va_arg(params, CCFiniteTimeActionEx*);
        if (pNow)
        {
            pPrev = create(pPrev, pNow);
        }
        else
        {
            break;
        }
    }
    
    va_end(params);
    return pPrev;
}

CCFiniteTimeActionEx* CCSequenceEx::actionWithArray(CCArray* arrayOfActions)
{
    return CCSequenceEx::create(arrayOfActions);
}

CCFiniteTimeActionEx* CCSequenceEx::create(CCArray* arrayOfActions)
{
    CCFiniteTimeActionEx* prev = (CCFiniteTimeActionEx*)arrayOfActions->objectAtIndex(0);
    
    for (unsigned int i = 1; i < arrayOfActions->count(); ++i)
    {
        prev = create(prev, (CCFiniteTimeActionEx*)arrayOfActions->objectAtIndex(i));
    }
    
    return prev;
}

bool CCSequenceEx::initOneTwo(CCFiniteTimeActionEx *pActionOne, CCFiniteTimeActionEx *pActionTwo)
{
    CCAssert(pActionOne != NULL, "");
    CCAssert(pActionTwo != NULL, "");
    
    int d = pActionOne->getDuration() + pActionTwo->getDuration();
    CCActionIntervalEx::initWithDuration(d);
    
    m_pActionsEx[0] = pActionOne;
    pActionOne->retain();
    
    m_pActionsEx[1] = pActionTwo;
    pActionTwo->retain();
    
    return true;
}

CCObject* CCSequenceEx::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCSequenceEx* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCSequenceEx*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCSequenceEx();
        pZone = pNewZone = new CCZone(pCopy);
    }
    
    CCActionIntervalEx::copyWithZone(pZone);
    
    pCopy->initOneTwo((CCFiniteTimeActionEx*)(m_pActionsEx[0]->copy()->autorelease()), 
                      (CCFiniteTimeActionEx*)(m_pActionsEx[1]->copy()->autorelease()));
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}

CCSequenceEx::~CCSequenceEx(void)
{
    CC_SAFE_RELEASE(m_pActionsEx[0]);
    CC_SAFE_RELEASE(m_pActionsEx[1]);
}

void CCSequenceEx::startWithTarget(CCNode *pTarget)
{
    CCActionIntervalEx::startWithTarget(pTarget);
    m_split = (float)m_pActionsEx[0]->getDuration() / (float)m_DurationFrames;
    m_last = -1;
}

void CCSequenceEx::stop(void)
{
    // Issue #1305
    if( m_last != - 1)
    {
        m_pActionsEx[m_last]->stop();
    }
    
    CCActionIntervalEx::stop();
}

void CCSequenceEx::update(float t)
{
    int found = 0;
    float new_t = 0.0f;
    
    if( t < m_split ) {
        // action[0]
        found = 0;
        if( m_split != 0 )
            new_t = t / m_split;
        else
            new_t = 1;
        
    } else {
        // action[1]
        found = 1;
        if ( m_split == 1 )
            new_t = 1;
        else
            new_t = (t-m_split) / (1 - m_split );
    }
    
    if ( found==1 ) {
        
        if( m_last == -1 ) {
            // action[0] was skipped, execute it.
            m_pActionsEx[0]->startWithTarget(m_pTarget);
            m_pActionsEx[0]->update(1.0f);
            m_pActionsEx[0]->stop();
        }
        else if( m_last == 0 )
        {
            // switching to action 1. stop action 0.
            m_pActionsEx[0]->update(1.0f);
            m_pActionsEx[0]->stop();
        }
    }
    
    // New action. Start it.
    if( found != m_last )
    {
        m_pActionsEx[found]->startWithTarget(m_pTarget);
    }
    
    m_pActionsEx[found]->update(new_t);
    m_last = found;
}

//
// MoveToEx
//
CCMoveToEx* CCMoveToEx::actionWithDuration(int durationframes, const CCPoint& position)
{
    return CCMoveToEx::create(durationframes, position);
}

CCMoveToEx* CCMoveToEx::create(int durationframes, const CCPoint& position)
{
    CCMoveToEx *pMoveTo = new CCMoveToEx();
    pMoveTo->initWithDuration(durationframes, position);
    pMoveTo->autorelease();
    
    return pMoveTo;
}

bool CCMoveToEx::initWithDuration(int durationframes, const CCPoint& position)
{
    if (CCActionIntervalEx::initWithDuration(durationframes))
    {
        m_endPosition = position;
        return true;
    }
    
    return false;
}

CCObject* CCMoveToEx::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCMoveToEx* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCMoveToEx*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCMoveToEx();
        pZone = pNewZone = new CCZone(pCopy);
    }
    
    CCActionIntervalEx::copyWithZone(pZone);
    
    pCopy->initWithDuration(m_DurationFrames, m_endPosition);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}

void CCMoveToEx::startWithTarget(CCNode *pTarget)
{
    CCActionIntervalEx::startWithTarget(pTarget);
    m_startPosition = pTarget->getPosition();
    m_delta = ccpSub(m_endPosition, m_startPosition);
}

void CCMoveToEx::update(float ratio)
{
    if (m_pTarget)
    {
        m_pTarget->setPosition(ccp(m_startPosition.x + m_delta.x * ratio,
                                   m_startPosition.y + m_delta.y * ratio));
    }
}

//
// MoveByEx
//
CCMoveByEx* CCMoveByEx::actionWithDuration(int durationframes, const CCPoint& position)
{
    return CCMoveByEx::create(durationframes, position);
}

CCMoveByEx* CCMoveByEx::create(int durationframes, const CCPoint& position)
{
    CCMoveByEx *pMoveBy = new CCMoveByEx();
    pMoveBy->initWithDuration(durationframes, position);
    pMoveBy->autorelease();
    
    return pMoveBy;
}

bool CCMoveByEx::initWithDuration(int durationframes, const CCPoint& position)
{
    if (CCActionIntervalEx::initWithDuration(durationframes))
    {
        m_delta = position;
        return true;
    }
    
    return false;
}

CCObject* CCMoveByEx::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCMoveByEx* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCMoveByEx*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCMoveByEx();
        pZone = pNewZone = new CCZone(pCopy);
    }
    
    CCMoveToEx::copyWithZone(pZone);
    
    pCopy->initWithDuration(m_DurationFrames, m_delta);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}

void CCMoveByEx::startWithTarget(CCNode *pTarget)
{
    CCPoint dTmp = m_delta;
    CCMoveToEx::startWithTarget(pTarget);
    m_delta = dTmp;
}

static inline float getBezierTrack(float *array, int from, int to, float t)
{
    if ( 1 == to - from)
    {
        return (1 - t) * array[from] + t * array[to];
    }
    else
        return (1 - t) * getBezierTrack(array, from, to-1, t) + t * getBezierTrack(array, from+1, to, t);
}

static inline float getRotateAngle(CCPoint curpos, CCPoint newpos)
{
    float angle;
    float disX = newpos.x - curpos.x;
    float disY = newpos.y - curpos.y;
    float dis = sqrt(pow(disX, 2) + pow(disY, 2));
    angle = acos(disY/dis);
    angle = disX > 0? angle : (2*PI - angle);
    angle = angle * 180 / PI;
    
    return angle;
}

//
// BezierByEx
//
CCBezierToEx* CCBezierToEx::actionWithDuration(int durationfrmes, CCArray *array)
{
    return CCBezierToEx::create(durationfrmes, array);
}

CCBezierToEx* CCBezierToEx::create(int durationfrmes, CCArray *array)
{
    CCBezierToEx *pBezierBy = new CCBezierToEx();
    pBezierBy->initWithDuration(durationfrmes, array);
    pBezierBy->autorelease();
    
    return pBezierBy;
}

bool CCBezierToEx::initWithDuration(int durationfrmes, CCArray *array)
{
    if (CCActionIntervalEx::initWithDuration(durationfrmes))
    {
        m_Array = array;
        
        CCObject *object = NULL;
        int count = 0;
        CCARRAY_FOREACH(m_Array, object)
        {
//            Move *point = (Move*)object;
//            m_arrayX[count] = point->getMovePoint().x;
//            m_arrayY[count++] = point->getMovePoint().y;
        }
        
        return true;
    }
    
    return false;
}

void CCBezierToEx::startWithTarget(CCNode *pTarget)
{
    CCActionIntervalEx::startWithTarget(pTarget);
    m_startPosition = pTarget->getPosition();
}

CCObject* CCBezierToEx::copyWithZone(CCZone *pZone)
{
    CCZone* pNewZone = NULL;
    CCBezierToEx* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCBezierToEx*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCBezierToEx();
        pZone = pNewZone = new CCZone(pCopy);
    }
    
    CCActionIntervalEx::copyWithZone(pZone);
    
    pCopy->initWithDuration(m_DurationFrames, m_Array);
    
    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}

void CCBezierToEx::update(float ratio)
{
    if (m_pTarget)
    {
        float x = getBezierTrack(m_arrayX, 0, m_Array->count()-1, ratio);
        float y = getBezierTrack(m_arrayY, 0, m_Array->count()-1, ratio);
        
        CCPoint curpos = m_pTarget->getPosition();
        float oldrotation = m_pTarget->getRotation();
        float newrotation = getRotateAngle(curpos, ccp(x, y));
        m_pTarget->setRotation(newrotation);
        m_pTarget->setPosition(ccp(x, y));
        
        float deltarotation = newrotation - oldrotation;
        if ( fabs(deltarotation) >181)
        {
            deltarotation = deltarotation > 0? deltarotation - 360 : deltarotation + 360;
        }
        
        if (0.01 < deltarotation) 
        {
            char framename[1024] = {0};
            sprintf(framename, "enemy%d_right.png", ((EnemyFighterSprite*)m_pTarget)->getFighterId());
            CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(framename);  
            ((EnemyFighterSprite*)m_pTarget)->setDisplayFrame(frame);  
        }
        else if (-0.01 > deltarotation)
        {
            char framename[1024] = {0};
            sprintf(framename, "enemy%d_left.png", ((EnemyFighterSprite*)m_pTarget)->getFighterId());
            CCSpriteFrame *frame=CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(framename);  
            ((EnemyFighterSprite*)m_pTarget)->setDisplayFrame(frame);  
        }
        else 
        {
            char framename[1024] = {0};
            sprintf(framename, "enemy%d.png", ((EnemyFighterSprite*)m_pTarget)->getFighterId());
            CCSpriteFrame *frame=CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(framename);  
            ((EnemyFighterSprite*)m_pTarget)->setDisplayFrame(frame);  
        }
    }
}

